"""
配置管理器 - 统一管理大模型和系统配置
支持多种 API 提供商、模型配置和环境配置
"""

import yaml
import os
from pathlib import Path
from typing import Dict, Any, Optional, List
from dataclasses import dataclass
import logging

logger = logging.getLogger(__name__)

@dataclass
class ModelConfig:
    """单个模型的配置"""
    name: str
    temperature: float
    max_tokens: int
    max_requests_per_minute: int
    timeout: int = 60

@dataclass
class EmbeddingConfig:
    """嵌入模型配置"""
    name: str
    dimensions: int
    max_batch_size: int

@dataclass
class ProviderConfig:
    """API 提供商配置"""
    api_key_env: str
    base_url: str
    models: Dict[str, ModelConfig]
    embedding_models: Dict[str, EmbeddingConfig]

@dataclass
class RetrievalConfig:
    """检索配置"""
    llm_reranking: bool
    llm_reranking_sample_size: int
    top_n_retrieval: int

@dataclass
class ProfileConfig:
    """完整的配置方案"""
    api_provider: str
    answering_model: str
    embedding_provider: str
    embedding_model: str
    parallel_requests: int
    retrieval: RetrievalConfig
    description: str

class ConfigManager:
    """配置管理器 - 负责加载和管理所有配置"""
    
    def __init__(self, config_path: Optional[str] = None):
        """
        初始化配置管理器
        
        Args:
            config_path: 配置文件路径，默认为 config/models.yaml
        """
        if config_path is None:
            # 从当前文件位置向上查找项目根目录
            current_dir = Path(__file__).parent
            project_root = current_dir.parent
            config_path = project_root / "config" / "models.yaml"
        
        self.config_path = Path(config_path)
        self.config = self._load_config()
        self._validate_config()
    
    def _load_config(self) -> Dict[str, Any]:
        """加载配置文件"""
        if not self.config_path.exists():
            raise FileNotFoundError(f"配置文件不存在: {self.config_path}")
        
        try:
            with open(self.config_path, 'r', encoding='utf-8') as f:
                config = yaml.safe_load(f)
            logger.info(f"成功加载配置文件: {self.config_path}")
            return config
        except yaml.YAMLError as e:
            raise ValueError(f"配置文件格式错误: {e}")
        except Exception as e:
            raise RuntimeError(f"加载配置文件失败: {e}")
    
    def _validate_config(self):
        """验证配置文件的完整性"""
        required_sections = ['providers', 'profiles', 'global_defaults', 'retrieval_defaults']
        
        for section in required_sections:
            if section not in self.config:
                raise ValueError(f"配置文件缺少必需的部分: {section}")
        
        # 验证至少有一个提供商和一个配置方案
        if not self.config['providers']:
            raise ValueError("配置文件中没有定义任何 API 提供商")
        
        if not self.config['profiles']:
            raise ValueError("配置文件中没有定义任何配置方案")
        
        logger.info("配置文件验证通过")
    
    def get_available_profiles(self) -> List[str]:
        """获取所有可用的配置方案名称"""
        return list(self.config['profiles'].keys())
    
    def get_profile_description(self, profile_name: str) -> str:
        """获取配置方案的描述"""
        if profile_name not in self.config['profiles']:
            raise ValueError(f"配置方案不存在: {profile_name}")
        
        return self.config['profiles'][profile_name].get('description', '无描述')
    
    def get_profile_config(self, profile_name: str = "default") -> ProfileConfig:
        """
        获取指定配置方案的完整配置
        
        Args:
            profile_name: 配置方案名称
            
        Returns:
            ProfileConfig: 配置方案对象
        """
        if profile_name not in self.config['profiles']:
            logger.warning(f"配置方案 '{profile_name}' 不存在，使用默认配置")
            profile_name = "default"
        
        profile_data = self.config['profiles'][profile_name]
        
        # 构建检索配置
        retrieval_config = RetrievalConfig(
            llm_reranking=profile_data['retrieval']['llm_reranking'],
            llm_reranking_sample_size=profile_data['retrieval']['llm_reranking_sample_size'],
            top_n_retrieval=profile_data['retrieval']['top_n_retrieval']
        )
        
        return ProfileConfig(
            api_provider=profile_data['api_provider'],
            answering_model=profile_data['answering_model'],
            embedding_provider=profile_data['embedding_provider'],
            embedding_model=profile_data['embedding_model'],
            parallel_requests=profile_data['parallel_requests'],
            retrieval=retrieval_config,
            description=profile_data.get('description', '无描述')
        )
    
    def get_model_config(self, provider: str, model_key: str) -> ModelConfig:
        """
        获取指定模型的配置
        
        Args:
            provider: API 提供商名称
            model_key: 模型键名
            
        Returns:
            ModelConfig: 模型配置对象
        """
        if provider not in self.config['providers']:
            raise ValueError(f"API 提供商不存在: {provider}")
        
        provider_config = self.config['providers'][provider]
        
        if model_key not in provider_config['models']:
            raise ValueError(f"模型不存在: {provider}/{model_key}")
        
        model_data = provider_config['models'][model_key]
        
        return ModelConfig(
            name=model_data['name'],
            temperature=model_data['temperature'],
            max_tokens=model_data['max_tokens'],
            max_requests_per_minute=model_data['max_requests_per_minute'],
            timeout=model_data.get('timeout', self.config['global_defaults']['timeout'])
        )
    
    def get_embedding_config(self, provider: str, model_key: str) -> EmbeddingConfig:
        """
        获取指定嵌入模型的配置
        
        Args:
            provider: API 提供商名称
            model_key: 嵌入模型键名
            
        Returns:
            EmbeddingConfig: 嵌入模型配置对象
        """
        if provider not in self.config['providers']:
            raise ValueError(f"API 提供商不存在: {provider}")
        
        provider_config = self.config['providers'][provider]
        
        if 'embedding_models' not in provider_config:
            raise ValueError(f"提供商 {provider} 没有定义嵌入模型")
        
        if model_key not in provider_config['embedding_models']:
            raise ValueError(f"嵌入模型不存在: {provider}/{model_key}")
        
        embedding_data = provider_config['embedding_models'][model_key]
        
        return EmbeddingConfig(
            name=embedding_data['name'],
            dimensions=embedding_data['dimensions'],
            max_batch_size=embedding_data['max_batch_size']
        )
    
    def get_provider_config(self, provider: str) -> ProviderConfig:
        """
        获取指定 API 提供商的配置
        
        Args:
            provider: API 提供商名称
            
        Returns:
            ProviderConfig: 提供商配置对象
        """
        if provider not in self.config['providers']:
            raise ValueError(f"API 提供商不存在: {provider}")
        
        provider_data = self.config['providers'][provider]
        
        # 构建模型配置字典
        models = {}
        for model_key, model_data in provider_data['models'].items():
            models[model_key] = ModelConfig(
                name=model_data['name'],
                temperature=model_data['temperature'],
                max_tokens=model_data['max_tokens'],
                max_requests_per_minute=model_data['max_requests_per_minute'],
                timeout=model_data.get('timeout', self.config['global_defaults']['timeout'])
            )
        
        # 构建嵌入模型配置字典
        embedding_models = {}
        if 'embedding_models' in provider_data:
            for emb_key, emb_data in provider_data['embedding_models'].items():
                embedding_models[emb_key] = EmbeddingConfig(
                    name=emb_data['name'],
                    dimensions=emb_data['dimensions'],
                    max_batch_size=emb_data['max_batch_size']
                )
        
        return ProviderConfig(
            api_key_env=provider_data['api_key_env'],
            base_url=provider_data['base_url'],
            models=models,
            embedding_models=embedding_models
        )
    
    def get_api_key(self, provider: str) -> Optional[str]:
        """
        获取指定提供商的 API 密钥
        
        Args:
            provider: API 提供商名称
            
        Returns:
            str: API 密钥，如果未设置则返回 None
        """
        provider_config = self.get_provider_config(provider)
        api_key = os.getenv(provider_config.api_key_env)
        
        if not api_key:
            logger.warning(f"未设置 {provider} 的 API 密钥环境变量: {provider_config.api_key_env}")
        
        return api_key
    
    def get_available_providers(self) -> List[str]:
        """获取所有可用的 API 提供商"""
        return list(self.config['providers'].keys())
    
    def get_available_models(self, provider: str) -> List[str]:
        """获取指定提供商的所有可用模型"""
        if provider not in self.config['providers']:
            return []
        
        return list(self.config['providers'][provider]['models'].keys())
    
    def get_available_embedding_models(self, provider: str) -> List[str]:
        """获取指定提供商的所有可用嵌入模型"""
        if provider not in self.config['providers']:
            return []
        
        provider_config = self.config['providers'][provider]
        if 'embedding_models' not in provider_config:
            return []
        
        return list(provider_config['embedding_models'].keys())
    
    def get_environment_config(self, env: str = "production") -> Dict[str, Any]:
        """
        获取环境特定配置
        
        Args:
            env: 环境名称 (development, production, testing)
            
        Returns:
            Dict: 环境配置
        """
        environments = self.config.get('environments', {})
        
        if env not in environments:
            logger.warning(f"环境配置 '{env}' 不存在，使用生产环境配置")
            env = "production"
        
        return environments.get(env, {})
    
    def reload_config(self):
        """重新加载配置文件"""
        logger.info("重新加载配置文件...")
        self.config = self._load_config()
        self._validate_config()
        logger.info("配置文件重新加载完成")
    
    def save_config(self, config_data: Dict[str, Any], backup: bool = True):
        """
        保存配置到文件
        
        Args:
            config_data: 要保存的配置数据
            backup: 是否创建备份文件
        """
        if backup and self.config_path.exists():
            backup_path = self.config_path.with_suffix('.yaml.backup')
            import shutil
            shutil.copy2(self.config_path, backup_path)
            logger.info(f"已创建配置文件备份: {backup_path}")
        
        try:
            with open(self.config_path, 'w', encoding='utf-8') as f:
                yaml.dump(config_data, f, default_flow_style=False, allow_unicode=True, indent=2)
            
            logger.info(f"配置文件已保存: {self.config_path}")
            self.reload_config()  # 重新加载以验证
            
        except Exception as e:
            logger.error(f"保存配置文件失败: {e}")
            raise

# 全局配置管理器实例
_config_manager = None

def get_config_manager() -> ConfigManager:
    """获取全局配置管理器实例（单例模式）"""
    global _config_manager
    if _config_manager is None:
        _config_manager = ConfigManager()
    return _config_manager

def reload_global_config():
    """重新加载全局配置"""
    global _config_manager
    if _config_manager is not None:
        _config_manager.reload_config()

# 便捷函数
def get_profile_config(profile_name: str = "default") -> ProfileConfig:
    """便捷函数：获取配置方案"""
    return get_config_manager().get_profile_config(profile_name)

def get_model_config(provider: str, model_key: str) -> ModelConfig:
    """便捷函数：获取模型配置"""
    return get_config_manager().get_model_config(provider, model_key)

def get_available_profiles() -> List[str]:
    """便捷函数：获取所有可用配置方案"""
    return get_config_manager().get_available_profiles()